
#pragma once

#include "base.h"

namespace l8
{


// A class that controls whether allocation is allowed.  This is for
// the C++ heap only!
class NativeAllocationChecker
{
public:
    typedef enum { ALLOW, DISALLOW } NativeAllocationAllowed;
    explicit inline NativeAllocationChecker(NativeAllocationAllowed allowed)
            : allowed_(allowed)
    {
#ifdef DEBUG
        if (allowed == DISALLOW)
        {
            allocation_disallowed_++;
        }
#endif
    }

    ~NativeAllocationChecker()
    {
#ifdef DEBUG
        if (allowed_ == DISALLOW)
        {
            allocation_disallowed_--;
        }
#endif
        ASSERT(allocation_disallowed_ >= 0);
    }

    static inline bool allocation_allowed()
    {
        return allocation_disallowed_ == 0;
    }

private:
    // This static counter ensures that NativeAllocationCheckers can be nested.
    static int allocation_disallowed_;

    // This flag applies to this particular instance.
    NativeAllocationAllowed allowed_;
};


// Superclass for classes managed with new & delete.
class Malloced
{
public:
    void* operator new(size_t size) { return New(size); }
    void  operator delete(void* p) { Delete(p); }

    static void FatalProcessOutOfMemory();
    static void* New(size_t size);
    static void Delete(void* p);
};

// Superclass for classes only using statics.
class AllStatic
{
#ifdef DEBUG
public:
    void* operator new(size_t size);
    void operator delete(void* p);
#endif
};

// A macro is used for defining the base class used for embedded instances.
// The reason is some compilers allocate a minimum of one word for the
// superclass. The macro prevents the use of new & delete in debug mode.
// In release mode we are not willing to pay this overhead.

#ifdef DEBUG
// Superclass for classes with instances allocated inside stack
// activations or inside other objects.
class Embedded
{
public:
    void* operator new(size_t size);
    void  operator delete(void* p);
};
#define BASE_EMBEDDED : public Embedded
#else
#define BASE_EMBEDDED
#endif

// Allocation policy for allocating in the C free store using malloc
// and free. Used as the default policy for lists.
class FreeStoreAllocationPolicy
{
public:
    static void* New(size_t size) { return Malloced::New(size); }
    static void Delete(void* p) { Malloced::Delete(p); }
};

// Allocation policy for allocating in preallocated space.
// Used as an allocation policy for ScopeInfo when generating
// stack traces.
class PreallocatedStorage : public AllStatic
{
public:
    explicit PreallocatedStorage(size_t size);

    size_t size() const
    { return size_; }

    static void* New(size_t size);

    static void Delete(void* p);

    // Preallocate a set number of bytes.
    static void Init(size_t size);

private:
    size_t size_;
    PreallocatedStorage* previous_;
    PreallocatedStorage* next_;
    static bool preallocated_;

    static PreallocatedStorage in_use_list_;
    static PreallocatedStorage free_list_;

    void LinkTo(PreallocatedStorage* other);
    void Unlink();

    DISALLOW_IMPLICIT_CONSTRUCTORS(PreallocatedStorage);
};


template <typename T>
static T * NewArray(size_t size)
{
    ASSERT(NativeAllocationChecker::allocation_allowed());
    T* result = new T[size];
    if (result == NULL)
    {
        Malloced::FatalProcessOutOfMemory();
    }

    return result;
}


template <typename T>
static void DeleteArray(T * array)
{
    delete[] array;
}


} // namespace l8
